/******************************************************************************/
/*                                                                            */
/*  Copyright (c) 2007-2009: ngi-central.org                                  */
/*  All rights reserved.                                                      */
/*                                                                            */
/******************************************************************************/


/// \file
/// This file implements data structures for moments calculations.


#ifndef __sgl_normalized_moments_h__
#define __sgl_normalized_moments_h__

#include <sgl/global_settings.h>
#include <sgl/conditions.h>
#include <sgl/unnormalized_moments.h>
#include <sgl/real_concept.h>
#include <boost/concept_check.hpp>

namespace sgl {


/******************************************************************************/
/*                                                                            */
/* normalized_moments                                                         */
/*                                                                            */
/******************************************************************************/

template <
	/// The type. 
	//! 
	//! This type defaults to SGL_FPTYPE (which in turn defaults to double), but 
	//! any other numeric type is also suitable.
	typename T=SGL_FPTYPE>
/// Class to hold normalized moments up to the third order.
//!
//! Normalized moments are not invariant with respect to translation, scaling 
//! and rotation.
//!
//! The following operations are implemented:
//! operator== : comparison for equality, directly implemented.
//! operator != : comparison for inequality, implemented through 
//! boost::equality_comaparable.
class normalized_moments
	: boost::equality_comparable<sgl::normalized_moments<T>, 
		sgl::normalized_moments<T>
	>
{
	// Only real types qualify for type T. If this line triggers an error,
	// type T is not a real type.
	BOOST_CONCEPT_ASSERT((sgl::real_concept<T>));

public:
	/// The value type.
	typedef T value_type;

	/// Default constructor. 
	//!
	//! Zero initializes the moments.
	normalized_moments()
		: m00_(T())
		, nm10_(T())
		, nm01_(T())
		, nm20_(T())
		, nm11_(T())
		, nm02_(T())
		, nm30_(T())
		, nm21_(T())
		, nm12_(T())
		, nm03_(T())
	{ }

	/// Standard constructor.
	//!
	//! Initializes the moments.
	normalized_moments(
		/// Zeroth order moment.
		T m00,
		/// First order horizontal moment.
		T nm10, 
		/// First order vertical moment.
		T nm01,
		/// Second order horizontal moment.
		T nm20, 
		/// Second order mixed moment.
		T nm11,
		/// Second order vertical moment.
		T nm02,
		/// Third order horizontal moment.
		T nm30, 
		/// Third order horizontal mixed moment.
		T nm21,
		/// Third order vertical mixed moment.
		T nm12,
		/// Third order vertical moment.
		T nm03)
		: m00_(m00)
		, nm10_(nm10)
		, nm01_(nm01)
		, nm20_(nm20)
		, nm11_(nm11)
		, nm02_(nm02)
		, nm30_(nm30)
		, nm21_(nm21)
		, nm12_(nm12)
		, nm03_(nm03)
	{ }

	/// Constructs normalized moments from unnormalized moments.
	//!
	//! This calculates normalized moments by dividing the respective 
	//! unnormalized moments by the zeroth order moment. The normalization
	//! will cause a division by zero, when the zeroth order moment is equal to 
	//! zero.
	normalized_moments(
		/// The right hand side.
		sgl::unnormalized_moments<T> const& r)
		: m00_(r.m00())
		, nm10_(r.calculate_nm10())
		, nm01_(r.calculate_nm01())
		, nm20_(r.calculate_nm20())
		, nm11_(r.calculate_nm11())
		, nm02_(r.calculate_nm02())
		, nm30_(r.calculate_nm30())
		, nm21_(r.calculate_nm21())
		, nm12_(r.calculate_nm12())
		, nm03_(r.calculate_nm03())
	{ }

	/// Constructs a copy.
	normalized_moments(
		/// The right hand side.
		sgl::normalized_moments<T> const& r)
		: m00_(r.m00_)
		, nm10_(r.nm10_)
		, nm01_(r.nm01_)
		, nm20_(r.nm20_)
		, nm11_(r.nm11_)
		, nm02_(r.nm02_)
		, nm30_(r.nm30_)
		, nm21_(r.nm21_)
		, nm12_(r.nm12_)
		, nm03_(r.nm03_)
	{ }

	/// Copy assignment
	sgl::normalized_moments<T> & operator =(
		/// The right hand side.
		sgl::normalized_moments<T> rhs)
	{
		swap(rhs);
		return *this;
	}

	/// Exchanges two objects.
	void swap(
		/// The other object.
		sgl::normalized_moments<T> & rhs)
	{
		std::swap(m00_, rhs.m00_);
		std::swap(nm10_, rhs.nm10_);
		std::swap(nm01_, rhs.nm01_);
		std::swap(nm20_, rhs.nm20_);
		std::swap(nm11_, rhs.nm11_);
		std::swap(nm02_, rhs.nm02_);
		std::swap(nm30_, rhs.nm30_);
		std::swap(nm21_, rhs.nm21_);
		std::swap(nm12_, rhs.nm12_);
		std::swap(nm03_, rhs.nm03_);
	}

	/// Compare objects for equality.
	//!
	//! This instance is equal to another instance, if its moments are all equal 
	//! to the moments of the other instance.
	//!
	//! Through the use of boost::equality_comparable operator != is also
	//! defined and implemented by calling this operator.
	//!
	//! \return true if equal, false otherwise.
    bool operator ==(
		/// Right hand side.
		sgl::normalized_moments<T> const& rhs) const
    {
        return 
			m00_ == rhs.m00_ && 
			nm10_ == rhs.nm10_ && 
			nm01_ == rhs.nm01_ &&
			nm20_ == rhs.nm20_ && 
			nm11_ == rhs.nm11_ &&
			nm02_ == rhs.nm02_ && 
			nm30_ == rhs.nm30_ && 
			nm21_ == rhs.nm21_ &&
			nm12_ == rhs.nm12_ &&
			nm03_ == rhs.nm03_;
    }

	/// Access the zeroth order moment.
	//!
	//! /return Returns a reference to the zeroth order moment.
	T & m00()
	{
		return m00_;
	}

	/// Access the zeroth order moment.
	//!
	//! /return Returns a const reference to the zeroth order moment.
	T const & m00() const
	{
		return m00_;
	}

	/// Access the horizontal first order moment.
	//!
	//! /return Returns a reference to the horizontal first order moment.
	T & nm10()
	{
		return nm10_;
	}

	/// Access the horizontal first order moment.
	//!
	//! /return Returns a const reference to the horizontal first order moment.
	T const & nm10() const
	{
		return nm10_;
	}

	/// Access the vertical first order moment.
	//!
	//! /return Returns a reference to the vertical first order moment.
	T & nm01()
	{
		return nm01_;
	}

	/// Access the vertical first order moment.
	//!
	//! /return Returns a const reference to the vertical first order moment.
	T const & nm01() const
	{
		return nm01_;
	}

	/// Access the horizontal second order moment.
	//!
	//! /return Returns a reference to the horizontal second order moment.
	T & nm20()
	{
		return nm20_;
	}

	/// Access the horizontal second order moment.
	//!
	//! /return Returns a const reference to the horizontal second order moment.
	T const & nm20() const
	{
		return nm20_;
	}

	/// Access the mixed second order moment.
	//!
	//! /return Returns a reference to the mixed second order moment.
	T & nm11()
	{
		return nm11_;
	}

	/// Access the mixed second order moment.
	//!
	//! /return Returns a const reference to the mixed second order moment.
	T const & nm11() const
	{
		return nm11_;
	}

	/// Access the vertical second order moment.
	//!
	//! /return Returns a reference to the vertical second order moment.
	T & nm02()
	{
		return nm02_;
	}

	/// Access the vertical second order moment.
	//!
	//! /return Returns a const reference to the vertical second order moment.
	T const & nm02() const
	{
		return nm02_;
	}

	/// Access the horizontal third order moment.
	//!
	//! /return Returns a reference to the horizontal third order moment.
	T & nm30()
	{
		return nm30_;
	}

	/// Access the horizontal third order moment.
	//!
	//! /return Returns a const reference to the horizontal third order moment.
	T const & nm30() const
	{
		return nm30_;
	}

	/// Access the first (horizontal) mixed third order moment.
	//!
	//! /return Returns a reference to the first (horizontal) mixed third order 
	//! moment.
	T & nm21()
	{
		return nm21_;
	}

	/// Access the first (horizontal) mixed third order moment.
	//!
	//! /return Returns a const reference to the first (horizontal) mixed third 
	//! order moment.
	T const & nm21() const
	{
		return nm21_;
	}

	/// Access the second (vertical) mixed third order moment.
	//!
	//! /return Returns a reference to the second (vertical) mixed third order 
	//! moment.
	T & nm12()
	{
		return nm12_;
	}

	/// Access the second (vertical) mixed third order moment.
	//!
	//! /return Returns a const reference to the second (vertical) mixed third 
	//! order moment.
	T const & nm12() const
	{
		return nm12_;
	}

	/// Access the vertical third order moment.
	//!
	//! /return Returns a reference to the vertical third order moment.
	T & nm03()
	{
		return nm03_;
	}

	/// Access the vertical third order moment.
	//!
	//! /return Returns a const reference to the vertical third order moment.
	T const & nm03() const
	{
		return nm03_;
	}

	/// Calculate mu00 from normalized moments.
	//!
	//! mu00 is the zeroth order central moment.
	//!
	//! The central moment mu00 is the same as the normalized one. This is
	//! slightly inconsistent, but the division by itself would otherwise bring
	//! this value to 1, which carries no information. However, the value is
	//! later needed for further normalization steps, which is why this function
	//! is provided.
	//!
	//! /return Zeroth order central moment.
	T calculate_mu00() const
	{
		return m00();
	}

	/// Calculate mu20 from normalized moments.
	//!
	//! mu20 is the second order horizontal central moment.
	//!
	//! /return Second order horizontal central moment.
	T calculate_mu20() const
	{
		return nm20() - nm10()*nm10();
	}

	/// Calculate mu11 from normalized moments.
	//!
	//! mu11 is the second order mixed central moment.
	//!
	//! /return Second order mixed central moment.
	T calculate_mu11() const
	{
		return nm11() - nm10()*nm01();
	}

	/// Calculate mu02 from normalized moments.
	//!
	//! mu20 is the second order vertical central moment.
	//!
	//! /return Second order vertical central moment.
	T calculate_mu02() const
	{
		return nm02() - nm01()*nm01();
	}

	/// Calculate mu30 from normalized moments.
	//!
	//! mu30 is the third order horizontal central moment.
	//!
	//! /return Third order horizontal central moment.
	T calculate_mu30() const
	{
		return T(2)*nm10()*nm10()*nm10() - T(3)*nm10()*nm20() + nm30();
	}

	/// Calculate mu21 from normalized moments.
	//!
	//! mu21 is the third order horizontal mixed central moment.
	//!
	//! /return Third order horizontal mixed central moment.
	T calculate_mu21() const
	{
		return T(2)*nm01()*nm10()*nm10() - T(2)*nm10()*nm11() - nm01()*nm20() + 
			nm21();
	}

	/// Calculate mu12 from normalized moments.
	//!
	//! mu12 is the third order vertical mixed central moment.
	//!
	//! /return Third order vertical mixed central moment.
	T calculate_mu12() const
	{
		return T(2)*nm01()*nm01()*nm10() - nm02()*nm10() - T(2)*nm01()*nm11() + 
			nm12();
	}

	/// Calculate mu03 from normalized moments.
	//!
	//! mu03 is the third order vertical central moment.
	//!
	//! /return Third order vertical central moment.
	T calculate_mu03() const
	{
		return T(2)*nm01()*nm01()*nm01() - T(3)*nm01()*nm02() + nm03();
	}

private:
	/// The zeroth order moment.
	T m00_;

	/// The horizontal first order moment.
	T nm10_;

	/// The vertical first order moment.
	T nm01_;

	/// The horizontal second order moment.
	T nm20_;

	/// The mixed second order moment.
	T nm11_;

	/// The vertical second order moment.
	T nm02_;

	/// The horizontal third order moment.
	T nm30_;

	/// The first (horizontal) mixed third order moment.
	T nm21_;

	/// The second (vertical) mixed third order moment.
	T nm12_;

	/// The vertical third order moment.
	T nm03_;
};


} // namespace sgl

#endif // __sgl_normalized_moments_h__
